#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <stdarg.h>
#include <time.h>
#include <math.h>
#include <stdint.h>
#include <FreeRTOS.h>
#include "task.h"
#include "main.h"
#include "sys.h"
#include "tools.h"
#include "sys_timer.h"
#include "onps.h"
#include "eth.h"
#define SYMBOL_GLOBALS
#include "emac.h"
#undef SYMBOL_GLOBALS

#if NETTOOLS_TELNETSRV
#include "at24c02.h"
#endif //* #if NETTOOLS_TELNETSRV

extern ETH_DMADESCTypeDef DMARxDscrTab[ETH_RXBUFNB];  //* Ethernet Rx MA Descriptor
extern ETH_DMADESCTypeDef DMATxDscrTab[ETH_TXBUFNB];  //* Ethernet Tx DMA Descriptor
extern uint8_t Rx_Buff[ETH_RXBUFNB][ETH_RX_BUF_SIZE]; //* Ethernet Receive Buffer
extern uint8_t Tx_Buff[ETH_TXBUFNB][ETH_TX_BUF_SIZE]; //* Ethernet Transmit Buffer

//* Global pointers to track current transmit and receive descriptors
extern ETH_DMADESCTypeDef *DMATxDescToSet;
extern ETH_DMADESCTypeDef *DMARxDescToGet;

//* Global pointer for last received frame infos
extern ETH_DMA_Rx_Frame_infos *DMA_RX_FRAME_infos;

//* mac地址
static const uint8_t lr_ubaMacAddr[ETH_MAC_ADDR_LEN] = {MAC_ADDR0_DEFAULT, MAC_ADDR1_DEFAULT, MAC_ADDR2_DEFAULT, MAC_ADDR3_DEFAULT, MAC_ADDR4_DEFAULT, MAC_ADDR5_DEFAULT};

//* 协议栈返回的netif结构
static PST_NETIF l_pstNetifEth = NULL;

//* ethernet网卡接收任务

static void start_thread_ethernet_ii_recv(void *pvParam)
{
    xTaskCreate(thread_ethernet_ii_recv, "EthRcv", 1024, pvParam, 6, NULL);
}

//* emac模块初始化
BOOL emac_init(void)
{
    uint16_t usTimeout = 0;
    ETH_InitTypeDef stMac;

   

    //* 填充结构体要设置的相关字段
    //* ------------------------ MAC -----------------------------------
    stMac.ETH_AutoNegotiation = ETH_AutoNegotiation_Enable; //* 10M/100M自适应
    stMac.ETH_LoopbackMode = ETH_LoopbackMode_Disable;
    stMac.ETH_RetryTransmission = ETH_RetryTransmission_Disable;
    stMac.ETH_AutomaticPadCRCStrip = ETH_AutomaticPadCRCStrip_Disable;
    // stMac.ETH_ReceiveAll               = ETH_ReceiveAll_Enable; //* 接收所有到达的帧，无论其是否已通过地址过滤，此时PromiscuousMode被禁止时，SA/DA仍然会将所有帧传送给上层应用，只不过SA/DA位会指示当前帧是否已通过地址过滤；PromiscuousMode使能则该位将被清零，无法指示
    stMac.ETH_ReceiveAll = ETH_ReceiveAll_Disable;
    stMac.ETH_BroadcastFramesReception = ETH_BroadcastFramesReception_Enable;
    stMac.ETH_PromiscuousMode = ETH_PromiscuousMode_Disable;          //* 禁止混杂模式，如此则地址过滤器将会指示当前到达的帧是否已通过地址过滤（否则SA/DA位会被清零）
    stMac.ETH_MulticastFramesFilter = ETH_MulticastFramesFilter_None; // ETH_MulticastFramesFilter_Perfect; //* 也可以开启ETH_ReceiveAll_Enable，这个地方依然是ETH_MulticastFramesFilter_Perfect，这样就能接收组播数据了
    stMac.ETH_UnicastFramesFilter = ETH_UnicastFramesFilter_Perfect;
    stMac.ETH_ChecksumOffload = ETH_ChecksumOffload_Enable; //* 在IP包收发时使用硬件校验和

    //*------------------------   DMA   -----------------------------------
    /* When we use the Checksum offload feature, we need to enable the Store and Forward mode:
    the store and forward guarantee that a whole frame is stored in the FIFO, so the MAC can insert/verify the checksum,
    if the checksum is OK the DMA can handle the frame otherwise the frame is dropped */
    stMac.ETH_DropTCPIPChecksumErrorFrame = ETH_DropTCPIPChecksumErrorFrame_Enable;
    stMac.ETH_ReceiveStoreForward = ETH_ReceiveStoreForward_Enable;
    stMac.ETH_TransmitStoreForward = ETH_TransmitStoreForward_Enable;
    stMac.ETH_ForwardErrorFrames = ETH_ForwardErrorFrames_Disable;
    stMac.ETH_ForwardUndersizedGoodFrames = ETH_ForwardUndersizedGoodFrames_Disable;
    stMac.ETH_SecondFrameOperate = ETH_SecondFrameOperate_Enable;
    stMac.ETH_AddressAlignedBeats = ETH_AddressAlignedBeats_Enable;
    stMac.ETH_FixedBurst = ETH_FixedBurst_Enable;
    stMac.ETH_RxDMABurstLength = ETH_RxDMABurstLength_32Beat;
    stMac.ETH_TxDMABurstLength = ETH_TxDMABurstLength_32Beat;
    stMac.ETH_DMAArbitration = ETH_DMAArbitration_RoundRobin_RxTx_2_1;

    //* Configure Ethernet
    while (ETH_ERROR == ETH_Init(&stMac, DP83848_PHY_ADDRESS))
        os_sleep_ms(10);

    //* Enable the Ethernet Rx Interrupt
    ETH_DMAITConfig(ETH_DMA_IT_NIS | ETH_DMA_IT_R, ENABLE);

    // ETH_MACAddressConfig(ETH_MAC_Address0, lr_ubaMacAddr);

    //* Initialize Tx Descriptors list: Chain Mode
    ETH_DMATxDescChainInit(DMATxDscrTab, &Tx_Buff[0][0], ETH_TXBUFNB);
    //* Initialize Rx Descriptors list: Chain Mode
    ETH_DMARxDescChainInit(DMARxDscrTab, &Rx_Buff[0][0], ETH_RXBUFNB);

    //* 发送报文的校验和插入操作旁路
    INT i;
    for (i = 0; i < ETH_TXBUFNB; i++)
        ETH_DMATxDescChecksumInsertionConfig(&DMATxDscrTab[i], ETH_DMATxDesc_ChecksumByPass /* ETH_DMATxDesc_ChecksumTCPUDPICMPFull */);

    //* 在ethernet启动之前先将其加入协议栈
    EN_ONPSERR enErr;
    CHAR bIsReqAddr = FALSE;

#if NETTOOLS_TELNETSRV
    ST_ETHCFG_IP stEthCfg;
    if (E2EthCfgReadIp(&stEthCfg)) //* 读取系统配置
    {
        if (stEthCfg.unAddr)
        {
            ST_IPV4 stIPv4;
            stIPv4.unAddr = stEthCfg.unAddr;
            stIPv4.unSubnetMask = stEthCfg.unSubnetMask;
            stIPv4.unGateway = stEthCfg.unGateway;
            stIPv4.unPrimaryDNS = stEthCfg.unPrimaryDns;
            stIPv4.unSecondaryDNS = stEthCfg.unSecondaryDns;
            stIPv4.unBroadcast = broadcast_addr(stIPv4.unAddr, stIPv4.unSubnetMask);
            l_pstNetifEth = ethernet_add(NETIF_ETH_NAME, stEthCfg.ubaMacAddr, &stIPv4, emac_send, start_thread_ethernet_ii_recv, &l_pstNetifEth, &enErr);

#if ETH_EXTRA_IP_EN
            if (l_pstNetifEth)
            {
                for (i = 0; i < ETH_EXTRA_IP_NUM; i++)
                {
                    if (stEthCfg.staExtraIp[i].unAddr)
                        netif_eth_add_ip(l_pstNetifEth, stEthCfg.staExtraIp[i].unAddr, stEthCfg.staExtraIp[i].unSubnetMask, NULL);
                    else
                        break;
                }
            }
#endif
        }
        else
        {
            l_pstNetifEth = ethernet_add(NETIF_ETH_NAME, stEthCfg.ubaMacAddr, NULL, emac_send, start_thread_ethernet_ii_recv, &l_pstNetifEth, &enErr);
            bIsReqAddr = TRUE;
        }

        if (l_pstNetifEth)
            E2EthCfgRouteTblLoad(l_pstNetifEth); //* 加载路由表
    }
    else
    {
        memset((UCHAR *)&stEthCfg, 0, sizeof(ST_ETHCFG_IP));

#endif //* #if NETTOOLS_TELNETSRV
#if !DHCP_REQ_ADDR_EN
        ST_IPV4 stIPv4;
        stIPv4.unAddr = inet_addr("192.168.0.4");
        stIPv4.unSubnetMask = inet_addr("255.255.255.0");
        stIPv4.unGateway = inet_addr("192.168.0.1");
        stIPv4.unPrimaryDNS = inet_addr(PRIMARY_DNS_DEFAULT);
        stIPv4.unSecondaryDNS = inet_addr(SECONDARY_DNS_DEFAULT);
        stIPv4.unBroadcast = broadcast_addr(stIPv4.unAddr, stIPv4.unSubnetMask);

#if NETTOOLS_TELNETSRV
        stEthCfg.unAddr = stIPv4.unAddr;
        stEthCfg.unSubnetMask = stIPv4.unSubnetMask;
        stEthCfg.unGateway = stIPv4.unGateway;
        stEthCfg.unPrimaryDNS = stIPv4.unPrimaryDNS;
        stEthCfg.unSecondaryDNS = stIPv4.unSecondaryDNS;
        memcpy(stEthCfg.ubaMacAddr, lr_ubaMacAddr, ETH_MAC_ADDR_LEN);
#endif //* #if NETTOOLS_TELNETSRV

        l_pstNetifEth = ethernet_add(NETIF_ETH_NAME, lr_ubaMacAddr, &stIPv4, emac_send, start_thread_ethernet_ii_recv, &l_pstNetifEth, &enErr);
#else //* #if !DHCP_REQ_ADDR_EN
#if NETTOOLS_TELNETSRV
    stEthCfg.unPrimaryDns = inet_addr(PRIMARY_DNS_DEFAULT);
    stEthCfg.unSecondaryDns = inet_addr(SECONDARY_DNS_DEFAULT);
    memcpy(stEthCfg.ubaMacAddr, lr_ubaMacAddr, ETH_MAC_ADDR_LEN);
#endif //* #if NETTOOLS_TELNETSRV

    l_pstNetifEth = ethernet_add(NETIF_ETH_NAME, lr_ubaMacAddr, NULL, emac_send, start_thread_ethernet_ii_recv, &l_pstNetifEth, &enErr);
    bIsReqAddr = TRUE;
#endif //* #if !DHCP_REQ_ADDR_EN
#if NETTOOLS_TELNETSRV
        if (!E2EthCfgWriteIp(&stEthCfg))
        {
#if SUPPORT_PRINTF
            printf("Ethernet configuration parameter writing failed.\r\n");
#endif
        }
    }
#endif //* #if NETTOOLS_TELNETSRV

    if (!l_pstNetifEth)
    {
#if SUPPORT_PRINTF
        printf("ethernet_add() failed, %s\r\n", onps_error(enErr));
#endif
        return FALSE;
    }

#if NETTOOLS_TELNETSRV
    ETH_MACAddressConfig(ETH_MAC_Address0, stEthCfg.ubaMacAddr);
#else
    ETH_MACAddressConfig(ETH_MAC_Address0, lr_ubaMacAddr);
#endif

    //* Enable MAC and DMA transmission and reception
    ETH_Start();

    while (bIsReqAddr)
    {
        if (dhcp_req_addr(l_pstNetifEth, &enErr))
        {
#if SUPPORT_PRINTF
            printf("dhcp request ip address successfully.\r\n");
#endif
            break;
        }
        else
        {
#if SUPPORT_PRINTF
            printf("dhcp request ip address failed, %s\r\n", onps_error(enErr));
#endif
            rt_thread_mdelay(6000);
        }
    }

    return TRUE;
}

void ETH_IRQHandler(void)
{
    __IO ETH_DMADESCTypeDef *DMARxNextDesc;
    UCHAR *pubPacket;
    EN_ONPSERR enErr;

    rt_interrupt_enter(); //* 进入中断
    {
        //* 处理所有到达的报文
        while (ETH_CheckFrameReceived())
        {
            //* 获取到达的一帧完整的ethernet ii协议数据
            FrameTypeDef stRcvedFrame = ETH_Get_Received_Frame();

            pubPacket = (UCHAR *)buddy_alloc(sizeof(ST_SLINKEDLIST_NODE) + stRcvedFrame.length, &enErr);
            if (pubPacket)
            {
                //* 搬运数据到接收链表
                PST_SLINKEDLIST_NODE pstNode = (PST_SLINKEDLIST_NODE)pubPacket;
                pstNode->uniData.unVal = stRcvedFrame.length;
                memcpy(pubPacket + sizeof(ST_SLINKEDLIST_NODE), (UCHAR *)stRcvedFrame.buffer, stRcvedFrame.length);
                ethernet_put_packet(l_pstNetifEth, pstNode);
            }
            else
            {
#if SUPPORT_PRINTF && DEBUG_LEVEL
                printf("<EIRQ> %s\r\n", onps_error(enErr));
#endif
            }

            //* 收到的报文传递给协议栈提供的ethernet ii层协议解析函数处理
            // ethernet_ii_recv(l_pstNetifEth, (UCHAR *)stRcvedFrame.buffer, (INT)stRcvedFrame.length);

            //* Release descriptors to DMA
            //* Check if frame with multiple DMA buffer segments
            if (DMA_RX_FRAME_infos->Seg_Count > 1)
            {
                DMARxNextDesc = DMA_RX_FRAME_infos->FS_Rx_Desc;
            }
            else
            {
                DMARxNextDesc = stRcvedFrame.descriptor;
            }

            //* Set Own bit in Rx descriptors: gives the buffers back to DMA
            for (int i = 0; i < DMA_RX_FRAME_infos->Seg_Count; i++)
            {
                DMARxNextDesc->Status = ETH_DMARxDesc_OWN;
                DMARxNextDesc = (ETH_DMADESCTypeDef *)(DMARxNextDesc->Buffer2NextDescAddr);
            }

            //* Clear Segment_Count
            DMA_RX_FRAME_infos->Seg_Count = 0;

            //* When Rx Buffer unavailable flag is set: clear it and resume reception
            if ((ETH->DMASR & ETH_DMASR_RBUS) != (u32)RESET)
            {
                //* Clear RBUS ETHERNET DMA flag
                ETH->DMASR = ETH_DMASR_RBUS;
                //* Resume DMA reception
                ETH->DMARPDR = 0;
            }
        }

        //* Clear the Eth DMA Rx IT pending bits
        ETH_DMAClearITPendingBit(ETH_DMA_IT_R);
        ETH_DMAClearITPendingBit(ETH_DMA_IT_NIS);
    }
    rt_interrupt_leave(); //* 离开中断
}

INT emac_send(SHORT sBufListHead, UCHAR *pubErr)
{
    SHORT sNextNode = sBufListHead;
    UCHAR *pubData;
    USHORT usDataLen;
    UCHAR *pubDMATxBuf = (UCHAR *)DMATxDescToSet->Buffer1Addr;
    __IO ETH_DMADESCTypeDef *DMATxNextDesc = DMATxDescToSet;
    UINT unHasCpyBytes = 0, unRemainBytes, unCpyBytes;

    //* 首先看看报文长度是否超出了DMA发送缓冲区的限制
    UINT unEthPacketLen = buf_list_get_len(sBufListHead);
    if (unEthPacketLen > ETH_TX_BUF_SIZE * ETH_TXBUFNB)
    {
        if (pubErr)
            *((EN_ONPSERR *)pubErr) = ERRPACKETTOOLARGE;
        return -1;
    }

    //* 遍历所有节点并将它们逐个放入DMA缓冲区
__lblGetNextNode:
    pubData = (UCHAR *)buf_list_get_next_node(&sNextNode, &usDataLen);
    if (NULL == pubData) //* 取出完毕，启动DMA发送
    {
        ETH_Prepare_Transmit_Descriptors((USHORT)unEthPacketLen);
        return (INT)unEthPacketLen;
        
    }
    unRemainBytes = (UINT)usDataLen;

__lblCpy:
    //* 当前DMA缓冲区链表节点是否已满，如果已满则移动到下一个节点继续接收
    if (unHasCpyBytes >= ETH_TX_BUF_SIZE)
    {
        //* 指向环形链表的下一个节点，不用考虑节点是否为空的问题，因为这个链表是一个全封闭的环形链表
        DMATxNextDesc = (ETH_DMADESCTypeDef *)(DMATxNextDesc->Buffer2NextDescAddr);
        pubDMATxBuf = (UCHAR *)DMATxNextDesc->Buffer1Addr;
        unHasCpyBytes = 0;
    }

    //* 计算能够copy的数据长度
    unCpyBytes = unRemainBytes < (ETH_TX_BUF_SIZE - unHasCpyBytes) ? unRemainBytes : (ETH_TX_BUF_SIZE - unHasCpyBytes);
    memcpy(pubDMATxBuf + unHasCpyBytes, pubData + (usDataLen - unRemainBytes), unCpyBytes);
    unRemainBytes -= unCpyBytes;
    unHasCpyBytes += unCpyBytes;
    if (!unRemainBytes)
        goto __lblGetNextNode;
    else
        goto __lblCpy;
}
